package drds.global_transaction.resource_manager.undolog.undo_executor;

import drds.global_transaction.resource_manager.model.Column;
import drds.global_transaction.resource_manager.model.Image;
import drds.global_transaction.resource_manager.model.KeyType;
import drds.global_transaction.resource_manager.model.Record;
import drds.global_transaction.resource_manager.undolog.UndoLog;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;

public abstract class UndoExecutor
{

	protected UndoLog undoLog;

	public UndoExecutor(UndoLog undoLog)
	{
		this.undoLog = undoLog;
	}

	protected abstract String buildUndoSql();

	protected void dataValidation(Connection connection) throws SQLException
	{
		// Validate if data is dirty.
	}

	public void executeOn(Connection connection) throws SQLException
	{
		dataValidation(connection);
		try
		{
			String undoSql = buildUndoSql();
			PreparedStatement preparedStatement = connection.prepareStatement(undoSql);
			Image undoImage = getUndoImage();
			for (Record record : undoImage.getRecordList())
			{
				ArrayList<Column> columnList = new ArrayList<>();
				Column primaryKeyValue = null;
				for (Column column : record.getColumnList())
				{
					if (column.getKeyType() == KeyType.primary_key)
					{
						primaryKeyValue = column;
					} else
					{
						columnList.add(column);
					}
				}
				setValuesForUndoPreparedStatement(preparedStatement, columnList, primaryKeyValue);
				preparedStatement.executeUpdate();
			}

		} catch (Exception ex)
		{
			if (ex instanceof SQLException)
			{
				throw (SQLException) ex;
			} else
			{
				throw new SQLException(ex);
			}

		}

	}

	protected abstract Image getUndoImage();

	protected void setValuesForUndoPreparedStatement(PreparedStatement preparedStatement, ArrayList<Column> columnList, Column primaryKeyValue) throws SQLException
	{
		int undoIndex = 0;
		for (Column column : columnList)
		{
			undoIndex++;
			preparedStatement.setObject(undoIndex, column.getValue(), column.getType());
		}
		// PK is at last one.
		// INSERT INTO a (x, y, z, pk) VALUES (?, ?, ?, ?)
		// UPDATE a SET x=?, y=?, z=? WHERE pk = ?
		// DELETE FROM a WHERE pk = ?
		undoIndex++;
		preparedStatement.setObject(undoIndex, primaryKeyValue.getValue(), primaryKeyValue.getType());
	}
}
